﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using AnimalsVoice.SDK;
// using System.Runtime.Loader;
using Microsoft.Extensions.DependencyInjection;

namespace CSharpConsoleTestSolution.Demo {
    /**
     * P30: 接口隔离, 反射, 特性, 依赖注入
     */
    public class P30_reflect_di {

        /**
         * 1.接口隔离: 显式接口实现, 时间: 32:00
         */
        public static void implement_interface_explicitly() {
            Console.WriteLine("1.接口隔离: 显式接口实现");
            Console.WriteLine("WarmKiller warm = new WarmKiller();");
            Console.WriteLine("IGentleman man = warm as IGentleman;");
            Console.WriteLine("IKiller killer = warm as IKiller;");
            Console.WriteLine("warm.Love();    //只能调用 Love()");
            Console.WriteLine("man.Love();     //只能调用 Love()");
            Console.WriteLine("killer.Kill();  //只能调用 Kill()");
            WarmKiller warm = new WarmKiller();
            IGentleman man = warm as IGentleman;
            IKiller killer = warm as IKiller;
            warm.Love();    //只能调用 Love()
            man.Love();     //只能调用 Love()
            killer.Kill();  //只能调用 Kill()
        }
        
        public interface IGentleman {
            void Love();
        }
        public interface IKiller {
            void Kill();
        }
        private class WarmKiller: IGentleman, IKiller {
            public void Love() {
                Console.WriteLine("I will love you for ever...");
            }
            /**
             * implement interface explicitly 显示地实现 IKiller:
             * 1.去掉 public
             * 2.只有将 WarmKiller 当作 IKiller 的实例来使用的时候, 才能调用 Kill()
             */
            /*public*/ void IKiller.Kill() {
                Console.WriteLine("Let me kill the enemy...");
            }
        }


        /**
         * 2.反射, 时间: 39:20
         */
        public static void reflection_test() {
            Console.WriteLine();
            Console.WriteLine("2.反射");
            
            var type = typeof(WarmKiller);
            //反射, 获取对象
            var o = Activator.CreateInstance(type);
            
            MethodInfo[] methods = type.GetMethods();
            //没有找到 Kill() 方法
            Console.Write("所有方法: ");
            foreach (var methodInfo in methods) { Console.Write($"{methodInfo.Name}, "); }
            Console.WriteLine();
            
            MethodInfo methodLove = type.GetMethod("Love");
            //没有 Kill 相关方法
            // MethodInfo methodKill = type.GetMethod("Kill");
            methodLove.Invoke(o, null);
            // methodKill.Invoke(o, null);
            var oIsKiller = o is IKiller;
            Console.WriteLine($"o is IKiller: {oIsKiller}");
            (o as IKiller)?.Kill();
        }

        /**
         * 3.框架: DependencyInjection, 时间: 52:50
         */
        public static void DependencyInjection_test() {
            Console.WriteLine();
            Console.WriteLine("3.框架: DependencyInjection");
            Console.WriteLine("在 NuGet 中搜索框架: DependencyInjection => Microsoft.Extensions.DependencyInjection, LinQ里面也依赖这个框架");
            Console.WriteLine("using Microsoft.Extensions.DependencyInjection;");
            Console.WriteLine("//容器, 应复用");
            Console.WriteLine("var sc = new ServiceCollection();");
            Console.WriteLine("sc.AddScoped(typeof(IGentleman), typeof(WarmKiller)); //添加 Gentleman");
            Console.WriteLine("sc.AddScoped(typeof(P28_abstract.IVehicle), typeof(P28_abstract.Car)); //添加Car, 可将 Car 替换成 Truck");
            Console.WriteLine("sc.AddScoped<P28_abstract.Driver>(); //添加司机");
            Console.WriteLine();
            Console.WriteLine("var sp = sc.BuildServiceProvider(); //应复用");
            Console.WriteLine();
            Console.WriteLine("//使用1");
            Console.WriteLine("IGentleman man = sp.GetService<IGentleman>();");
            Console.WriteLine("man.Love();");
            
            
            //容器, 应复用
            var sc = new ServiceCollection();
            sc.AddScoped(typeof(IGentleman), typeof(WarmKiller)); //添加 Gentleman
            sc.AddScoped(typeof(P28_abstract.IVehicle), typeof(P28_abstract.Car)); //添加Car, 可将 Car 替换成 Truck
            // sc.AddScoped(typeof(P28_abstract.IVehicle), typeof(P28_abstract.Truck)); //可将 Car 替换成 Truck
            sc.AddScoped<P28_abstract.Driver>(); //添加司机
            
            var sp = sc.BuildServiceProvider(); //应复用

            //使用1
            IGentleman man = sp.GetService<IGentleman>();
            man.Love();
            Console.WriteLine($"isKiller: {man is IKiller}");
            (man as IKiller)?.Kill();
            
            
            Console.WriteLine();
            Console.WriteLine("//使用2");
            Console.WriteLine("var driver = sp.GetService<P28_abstract.Driver>();  //构造方法不用设置IVehicle, 自动填充了!");
            Console.WriteLine("driver.Drive();");
            
            //使用2
            // var vehicle = sp.GetService<P28_abstract.IVehicle>();
            var driver = sp.GetService<P28_abstract.Driver>();  //构造方法不用设置IVehicle, 自动填充了!
            driver.Drive();
        }


        /**
         * 4.使用依赖注入, 常用于"插件式编程", 时间: 1:01:58
         * 见 Animals_Cat_Sheep_Voices.Lib, Animals_Voice_Use_SDK.Lib
         * 需要编译以上2个Lib, 然后放入Animals目录下!
         */
        public static void plugins_test() {
            Console.WriteLine();
            Console.WriteLine("4.使用依赖注入, 常用于\"插件式编程\"");
            Console.WriteLine("见 Animals_Cat_Sheep_Voices.Lib, Animals_Voice_Use_SDK.Lib");
            var currentDirectory = Environment.CurrentDirectory;
            Console.WriteLine($"currentDirectory: {currentDirectory}");

            var folder = Path.Combine(currentDirectory, "Animals");
            if (!Directory.Exists(folder)) {
                DirectoryInfo info = Directory.CreateDirectory(folder);
                Console.WriteLine($"还没有Animals目录, 创建结果: {info.Exists}");
                return;
            }
            var files = Directory.GetFiles(folder);
            var animalTypes = new List<Type>();
            foreach (var file in files) {
                //需要添加依赖: System.Runtime.Loader
                //TODO: 怎么一直报错, 导入不进来? 无语.
            // var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
            
                Assembly assembly = Assembly.LoadFile(file);
                var types = assembly.GetTypes();
                foreach (var type in types) {
                    var methodInfo = type.GetMethod("Voice");
                    if (methodInfo != null) {
                        animalTypes.Add(type);
                    }
                }
            }
            
            //开始玩游戏
            if (animalTypes.Count == 0) {
                Console.WriteLine("还没有游戏哦, 需要编译2个Animals...Lib, 然后放入Animals目录下!");
            } else {
                while (true) {
                    Console.WriteLine("╔═══════════════════════════════");
                    Console.WriteLine("╠═请选择动物, exit退出:");
                    for (var i = 0; i < animalTypes.Count; i++) {
                        Console.WriteLine($"╠═ {i + 1}.{animalTypes[i].Name}");
                    }
                    Console.WriteLine("╚════════════════════════════════");
                    var position = 0;
                    var input = Console.ReadLine();
                    if (input == "exit") {
                        break;
                    }
                    var success = int.TryParse(input, out position);
                    if (!success) {
                        Console.WriteLine("请输入正确的数字!");
                        Console.WriteLine();
                        continue;
                    }
                    if (position < 1 || position > animalTypes.Count) {
                        Console.WriteLine("索引越界!");
                        Console.WriteLine();
                        continue;
                    }
                    Type animalType = animalTypes[position - 1];
                    MethodInfo method = animalType.GetMethod("Voice");
                    var o = Activator.CreateInstance(animalType);
                    method.Invoke(o, new Object[]{ 3 }); //参2: 播放3遍
                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
        }



        /**
         * 5.SDK 开发, 时间: 1:21:30
         * 由于上方使用的反射, 容易犯错误, 所以应该使用 sdk 的方式!
         * 见  AnimalsVoice.SDK
         * 1.CSharpConsoleTestSolution 自己的项目需要引入 sdk: Dependencies -> Reference
         * 2.编译 sdk, 然后把 sdk 给用户, 用户放入项目/libs/目录下(非固定目录), Dependencies -> Reference -> Add Form -> 找到Xxx.dll,
         *   会在项目的.csproj 中添加.dll 的引用
         */
        public static void sdk_test() {
            Console.WriteLine();
            Console.WriteLine("5.SDK 开发");
            Console.WriteLine("见 AnimalsVoice.SDK");
            var currentDirectory = Environment.CurrentDirectory;
            Console.WriteLine($"currentDirectory: {currentDirectory}");

            var folder = Path.Combine(currentDirectory, "Animals");
            if (!Directory.Exists(folder)) {
                DirectoryInfo info = Directory.CreateDirectory(folder);
                Console.WriteLine($"还没有Animals目录, 创建结果: {info.Exists}");
                return;
            }
            var files = Directory.GetFiles(folder);
            var animalTypes = new List<Type>();
            foreach (var file in files) {
                //需要添加依赖: System.Runtime.Loader
                //TODO: 怎么一直报错, 导入不进来? 无语.
            // var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
            
                Assembly assembly = Assembly.LoadFile(file);
                var types = assembly.GetTypes();
                Type typeIAnimal = typeof(IAnimalsVoice);
                Type typeUnfinished = typeof(UnfinishedAttribute);
                foreach (var type in types) {
                    Type[] interfaces = type.GetInterfaces();
                    //这儿直接判断是否实现 IAnimalsVoice,
                    if (interfaces.Contains(typeIAnimal)) {
                        //inherit: 是否搜索此成员的继承链以查找属性
                        Attribute attribute = type.GetCustomAttribute(typeUnfinished, true);
                        //下方的也可以
                        // object[] attributes = type.GetCustomAttributes(false);
                        // for (var i = 0; i < attributes.Length; i++) {
                        //     Console.Write($"attribute[{i}]={attributes[i]}, ");
                        // }
                        // bool isUnfinished = attributes.Any(a => a.GetType() == typeUnfinished);

                        //if没有这个 Attribute
                        // if (isUnfinished) {
                        if (attribute == null) {
                            animalTypes.Add(type);
                        } else {
                            Console.WriteLine($"type: {type}, 找到attribute: {attribute.GetType().Name}");
                        }
                    }
                }
            }
            
            //开始玩游戏
            if (animalTypes.Count == 0) {
                Console.WriteLine("还没有游戏哦, 需要编译2个Animals...Lib, 然后放入Animals目录下!");
            } else {
                while (true) {
                    Console.WriteLine("╔═══════════════════════════════");
                    Console.WriteLine("╠═请选择动物, exit退出:");
                    for (var i = 0; i < animalTypes.Count; i++) {
                        Console.WriteLine($"╠═ {i + 1}.{animalTypes[i].Name}");
                    }
                    Console.WriteLine("╚════════════════════════════════");
                    var position = 0;
                    var input = Console.ReadLine();
                    if (input == "exit") {
                        break;
                    }
                    var success = int.TryParse(input, out position);
                    if (!success) {
                        Console.WriteLine("请输入正确的数字!");
                        Console.WriteLine();
                        continue;
                    }
                    if (position < 1 || position > animalTypes.Count) {
                        Console.WriteLine("索引越界!");
                        Console.WriteLine();
                        continue;
                    }
                    Type animalType = animalTypes[position - 1];
                    //直接强转 IAnimalsVoice, 然后调用方法
                    var o = Activator.CreateInstance(animalType) as IAnimalsVoice;
                    o.Voice(3); //参2: 播放3遍
                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
        }
    }
}